package jmine.tec.script.impl.beanshell;

import java.util.HashMap;
import java.util.Map;

import bsh.EvalError;
import bsh.Interpreter;

/**
 * Classe utilitária para converter de Wrappers para tipo primitivos.
 * 
 * @author julien
 */
public final class PrimitiveAdapter {

    private static final Map<Class<?>, SetAsPrimitive> SETTERS = new HashMap<Class<?>, SetAsPrimitive>();

    static {
        SETTERS.put(Integer.class, new SetAsPrimitiveInteger());
        SETTERS.put(Short.class, new SetAsPrimitiveShort());
        SETTERS.put(Double.class, new SetAsPrimitiveDouble());
        SETTERS.put(Long.class, new SetAsPrimitiveLong());
        SETTERS.put(Float.class, new SetAsPrimitiveFloat());
        SETTERS.put(Byte.class, new SetAsPrimitiveByte());
        SETTERS.put(Character.class, new SetAsPrimitiveCharacter());
        SETTERS.put(Boolean.class, new SetAsPrimitiveBoolean());
    }

    /**
     * C'tor
     */
    private PrimitiveAdapter() {
    }

    /**
     * Seta este valor com este nome neste interpreter convertendo pra primitivo se necessário
     * 
     * @param interpreter {@link Interpreter}
     * @param nome String
     * @param valor Object
     * @throws EvalError Se não for possível setar a variável
     */
    public static void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
        if (valor == null) {
            return;
        }
        SetAsPrimitive setter = SETTERS.get(valor.getClass());
        if (setter != null) {
            setter.setAsPrimitive(interpreter, nome, valor);
        } else {
            interpreter.set(nome, valor);
        }
    }

    /**
     * @author julien
     */
    private static interface SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para o primitivo adequado
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError;
    }

    /**
     * Setter para Integer
     * 
     * @author julien
     */
    private static class SetAsPrimitiveInteger implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para int
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Integer) valor).intValue());
        }
    }

    /**
     * Setter para Byte
     * 
     * @author julien
     */
    private static class SetAsPrimitiveByte implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para byte
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Byte) valor).byteValue());
        }
    }

    /**
     * Setter para Short
     * 
     * @author julien
     */
    private static class SetAsPrimitiveShort implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para short
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Short) valor).shortValue());
        }
    }

    /**
     * Setter para Long
     * 
     * @author julien
     */
    private static class SetAsPrimitiveLong implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para long
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Long) valor).longValue());
        }
    }

    /**
     * Setter para Float
     * 
     * @author julien
     */
    private static class SetAsPrimitiveFloat implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para float
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Float) valor).floatValue());
        }
    }

    /**
     * Setter para Double
     * 
     * @author julien
     */
    private static class SetAsPrimitiveDouble implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para double
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Double) valor).doubleValue());
        }
    }

    /**
     * Setter para Character
     * 
     * @author julien
     */
    private static class SetAsPrimitiveCharacter implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para char
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Character) valor).charValue());
        }
    }

    /**
     * Setter para Boolean
     * 
     * @author julien
     */
    private static class SetAsPrimitiveBoolean implements SetAsPrimitive {
        /**
         * Seta este valor com este nome neste interpreter convertendo para boolean
         * 
         * @param interpreter {@link Interpreter}
         * @param nome String
         * @param valor Object
         * @throws EvalError Se não for possível setar a variável
         */
        public void setAsPrimitive(final Interpreter interpreter, final String nome, final Object valor) throws EvalError {
            interpreter.set(nome, ((Boolean) valor).booleanValue());
        }
    }
}
